package coalition.formation.coalitions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import coalition.formation.combinations.Combination;
import coalition.formation.combinations.IndexedCombination;
import coalition.formation.combinations.SuperSet;
import coalition.formation.externality.Externality;
import coalition.formation.partitions.Partition;
import coalition.formation.partitions.PartitionDecorator;




public class MergeProof {

	Externality externality;
	int numElements;

	public MergeProof(Externality externality){
		this.externality = externality;
		this.numElements = externality.getSize();
	}
	
	public List<int[]> getMergeProofPartitions(){
		Partition testPartition = new Partition(numElements);
		ArrayList<int[]> partitions = testPartition.getPartitions();
		return getMergeProofPartitions(partitions);
	}
	
	public List<int[]> getMergeProofPartitions(List<int[]> partitions){
		List<int[]> result = new ArrayList<int[]>();
		
		List<int[]> combinations = SuperSet.getSuperSetMinusEmptySet(numElements);
			
		for(int[] partition : partitions){
			System.out.println("starting partition:" + coalition.formation.utils.Util.printIntArray(partition));
			float[] thisExternalities = externality.getExternalities(partition);
			System.out.println("\texternalities:" + coalition.formation.utils.Util.printFloatArray(thisExternalities)+ "\n");
			boolean isCore = true;
			//check every subset of coalitions
			int numCoalitions = PartitionDecorator.getNumCoalitions(partition);
			for(int[] coalitions: Combination.findCombinations(numCoalitions)){
				
				//don't consider merger of zero or one coalition
				if(coalitions.length == 0 || coalitions.length == 1) continue;			
			
				int[] otherPartition = joinCoalitions(partition, coalitions);
				int[] mergeIndicatorArray = getMergeIndicatorArray(partition, coalitions);
				System.out.println("\t\tmerge indicator:" + coalition.formation.utils.Util.printIntArray(mergeIndicatorArray));
				System.out.println("\t\tcoalition list:" + coalition.formation.utils.Util.printIntArray(coalitions));
				System.out.println("\t\tresulting partition:" + coalition.formation.utils.Util.printIntArray(otherPartition) );					
				
				float[] otherExternalities = externality.getExternalities(otherPartition);
				
				boolean isGreaterThan = Core.floatArrayGreaterThan(otherExternalities, thisExternalities, coalitions);
				String cmpSymbol = isGreaterThan ? " > " : " !> ";
				System.out.println("\t\tresulting externalities:" + 
						coalition.formation.utils.Util.printFloatArray(otherExternalities, mergeIndicatorArray) + 
						cmpSymbol + 
						coalition.formation.utils.Util.printFloatArray(thisExternalities, mergeIndicatorArray)+ 
						"\n");					
				
				if(isGreaterThan){
					isCore = false; 
					System.out.println("\t\tviolates split-proof-ness");
					//break;
				
				}
			}		
			if(isCore)
				result.add(partition);					
		}
		
		return result;
	}
	
	static int[] getMergeIndicatorArray(int[] partition, int[] coalitionIndices){
		int[] mergeIndicatorArray = new int[partition.length];
		for(int i = 0; i < partition.length; i++){
			for(int j = 0; j < coalitionIndices.length; j++){
				//use an index one greater than anything present in
				//a normalized partition; this new index will represent
				//the splitting group
				if(partition[i] == coalitionIndices[j]){
					mergeIndicatorArray[i] = 1;
					break;
				}
			}
		}	

		return mergeIndicatorArray;
	}
	
	static int[] joinCoalitions(int[] partition, int[] coalitionIndices){
		int[] newPartition = Arrays.copyOf(partition, partition.length);
		for(int i = 0; i < partition.length; i++){
			for(int j = 0; j < coalitionIndices.length; j++){
				//use an index one greater than anything present in
				//a normalized partition; this new index will represent
				//the splitting group
				if(partition[i] == coalitionIndices[j]){
					newPartition[i] = partition.length;
					break;
				}
			}
		}	
		int[] normalizedPartition = PartitionDecorator.reduceIndices(newPartition);
		return normalizedPartition;
	}
	
}
